import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;

public class ClojureWrapper {
	public static final int BUFFER_SIZE = 16 * 1024;

	private PrintWriter consizeInputWriter;
	private BufferedReader consizeOutputReader;
	private BufferedReader consizeErrorReader;
	private Thread outputReaderThread;
	private Thread errorReaderThread;
	private ClojureListener listener;

	public void init() throws IOException {
		PipedOutputStream outStream = new PipedOutputStream();
		consizeInputWriter = new PrintWriter(outStream);
		PipedInputStream inStream = new PipedInputStream(outStream);
		System.setIn(inStream);

		PipedInputStream inputStream = new PipedInputStream();
		consizeOutputReader = new BufferedReader(new InputStreamReader(
				inputStream));
		PipedOutputStream outStream2 = new PipedOutputStream(inputStream);
		System.setOut(new PrintStream(outStream2));

		PipedInputStream inputStream2 = new PipedInputStream();
		consizeErrorReader = new BufferedReader(new InputStreamReader(
				inputStream2));
		PipedOutputStream outStream3 = new PipedOutputStream(inputStream2);
		System.setErr(new PrintStream(outStream3));
		
		createAndStartOutputHandler();
	}

	public void startConsize() {
		new Thread(){
			@Override
			public void run() {
				clojure.main.main(new String[] { "consize.clj" });
				listener.consizeEnded();
			}
		}.start();
	}
	
	public void sendInput(String input){
		consizeInputWriter.print(input+'\n');
	}

	private void createAndStartOutputHandler() {
		outputReaderThread = new Thread() {
			public void run() {
				char[] buffer = new char[BUFFER_SIZE];
				while (true)
					try {
						int readLine = consizeOutputReader.read(buffer);
						String string = new String(buffer, 0, readLine);
						getListener().receiveOutput(string);
					} catch (IOException e) {
						e.printStackTrace();
					}
			};
		};
		outputReaderThread.start();

		errorReaderThread = new Thread() {
			public void run() {
				char[] buffer = new char[BUFFER_SIZE];
				while (true)
					try {
						int readLine = consizeErrorReader.read(buffer);
						String string = new String(buffer, 0, readLine);
						getListener().receiveError(string);
					} catch (IOException e) {
						e.printStackTrace();
					}
			};
		};
		errorReaderThread.start();
	}

	public ClojureListener getListener() {
		return listener;
	}

	public void setListener(ClojureListener listener) {
		this.listener = listener;
	}
}
